package com.alpha.gateway.service;

import com.alpha.gateway.schemas.whitelists.WhitelistService;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.util.AntPathMatcher;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 用户授权(验证某个用户是否有权限执行某个操作，即判断用户是否有权限去做某些事情)
 *
 * @author chenruwan
 * @since 2022-01-01
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class AuthorizationService {
    private final AntPathMatcher pathMatcher = new AntPathMatcher();
    private final AuthorizationCacheAdapter cacheAdapter;

    public boolean isWhitelists(ServerHttpRequest request) {
        return cacheAdapter.getWhitelists().stream().anyMatch(whitelist -> matchable(whitelist, request));
    }

    public boolean hasAuthority(String userId, ServerHttpRequest request) {
        Optional<RequestAttr> optional = cacheAdapter.getAllResources().stream()
                .filter(res -> matchable(res, request)).findFirst();
        if (optional.isEmpty()) {
            return false;
        }

        return cacheAdapter.getUserResources(userId).stream().anyMatch(res -> res.equals(optional.get()));
    }

    private boolean matchable(RequestAttr matcher, ServerHttpRequest request) {
        return matcher.getMethod().equals(request.getMethodValue()) &&
                pathMatcher.match(matcher.getUrl(), request.getPath().value());
    }

    @Data
    @EqualsAndHashCode
    @NoArgsConstructor
    @AllArgsConstructor
    static class RequestAttr {
        String url;
        String method;
    }

    @Component
    @RequiredArgsConstructor
    static class AuthorizationCacheAdapter {
        private final WhitelistService whitelistService;
        private final FeignClientService feignClientService;

        @Cacheable("whitelists")
        public List<RequestAttr> getWhitelists() {
            return whitelistService.list().stream()
                    .map(e -> new RequestAttr(e.getUrl(), e.getMethod()))
                    .collect(Collectors.toList());
        }

        @Cacheable("resources")
        public List<RequestAttr> getAllResources() {
            return feignClientService.getAllResources().stream()
                    .map(e -> new RequestAttr(e.getUrl(), e.getMethod()))
                    .sorted(Comparator.comparing(RequestAttr::getUrl))
                    .collect(Collectors.toList());
        }

        @Cacheable(value = "users::resources", key = "#userId")
        public List<RequestAttr> getUserResources(String userId) {
            return feignClientService.getUserResources(userId).stream()
                    .map(e -> new RequestAttr(e.getUrl(), e.getMethod()))
                    .collect(Collectors.toList());
        }
    }
}
